package com.ys168.gam.download;

import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.ys168.gam.bean.JobInfo;
import com.ys168.gam.util.DownloadUtil;

/**
 * 
 * @author Jason Krebs
 * @since 2015年6月10日
 */
public class Downloader {

    class DownloadExecutor implements Runnable {

        private synchronized void activeAllDownloadFinishedEvent() {
            if (unFinishedCount == 0) {
                downloadEventSource.activeAllDownloadFinishedEvent();
            }
        }

        private synchronized void activeDownloadFinishedEvent(DownloadEvent event) {
            if (event.isDownloadSuccessed()) {
                successedCount++;
            }
            else if (event.isDownloadFailed()) {
                failedCount++;
            }
            unFinishedCount--;
            event.setSuccessedCount(successedCount);
            event.setFailedCount(failedCount);

            downloadEventSource.activeDownloadFinishedEvent(event);
        }

        @Override
        public void run() {
            JobInfo jobInfo = null;

            while ((jobInfo = allocateJob()) != null) {
                DownloadEvent event = new DownloadEvent(jobInfo.getUrl(), jobInfo.getIndex());
                event.setStatus(DownloadStatus.DOWNLOADING);
                downloadEventSource.activeDownloadingEvent(event);

                try {
                    DownloadUtil.download(jobInfo, savePath);
                }
                catch (Exception e) {// download error
                    e.printStackTrace();

                    event.setStatus(DownloadStatus.DOWNLOAD_FAILED);
                    event.setMessage(e.getMessage());
                    activeDownloadFinishedEvent(event);
                    continue;
                }

                event.setStatus(DownloadStatus.DOWNLOADED_SUCCESSED);
                activeDownloadFinishedEvent(event);

            }
            activeAllDownloadFinishedEvent();
        }
    }

    private ExecutorService exec = Executors.newCachedThreadPool();
    private DownloadEventSource downloadEventSource = new DownloadEventSource();

    private List<JobInfo> jobInfos;
    private String savePath;
    private int unFinishedCount;
    private int successedCount;
    private int failedCount;

    public Downloader(List<JobInfo> jobInfos, String savePath) {
        this.jobInfos = new LinkedList<JobInfo>(jobInfos);
        this.savePath = savePath;
        this.unFinishedCount = jobInfos.size();
        this.successedCount = 0;
        this.failedCount = 0;

        tryValidateSavePath();
    }

    public void addListener(IDownloadListener downloadListener) {
        downloadEventSource.addListener(downloadListener);
    }

    private synchronized JobInfo allocateJob() {
        if (jobInfos.isEmpty()) {
            return null;
        }
        return jobInfos.remove(0);
    }

    public void removeListener(IDownloadListener downloadListener) {
        downloadEventSource.removeListener(downloadListener);
    }

    public void start(int threadCount) {
        for (int i = 0; i < Math.min(threadCount, jobInfos.size()); i++) {
            exec.execute(new DownloadExecutor());
        }
        exec.shutdown();
    }

    private void tryValidateSavePath() {
        File dir = new File(savePath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
    }

}
